package mmc;

import java.io.Serializable;

import org.lwjgl.opengl.GL11;

import mmc.physics.CenteredSphere;
import mmc.physics.PhysicsReactionType;
import mmc.physics.PhysicsType;
import mmc.physics.Position;
import mmc.server.Server;
import mmc.stats.StandardStats;

@SuppressWarnings("serial")
public abstract class EngineObject implements Serializable, Comparable<EngineObject>{
	public float x, y, z;
	public int id;
	public float health = 1;
	public float energy = 1;
	
	public transient PhysicsReactionType reactionType = PhysicsReactionType.ABSORB;
	public transient PhysicsType physicsType = PhysicsType.STATIC;
	public transient CenteredSphere boundingSphere;
	
	/**
	 * Objects flagged as dirty on the server are transmitted to clients in range.
	 */
	public transient boolean dirty;
	
	public EngineObject(int id, float x, float y, float z){
		this.id = id;
		this.x=x;
		this.y=y;
		this.z=z;
		
		boundingSphere = new CenteredSphere(this, 0);
	}
	
	public EngineObject(float x, float y, float z){
		this(Constants.IS_SERVER?generateId():-1,x,y,z);
	}
	
	public void initOnClient(Scene scene){
		
	}
	
	public abstract void render(EngineObject observer);
	public abstract void update(float dt);
	
	public void update(Server server, float dt){
		
	}
	
	public boolean inRange(EngineObject other, float range){
		float dx = x-other.x,
			  dy = y-other.y,
			  dz = z-other.z;
		
		return dx*dx+dy*dy+dz*dz < range*range; 
	}
	
	private static int currentId = 0;
	private static synchronized int generateId(){
		return currentId++;
	}
	
	public int getId(){
		if(id==-1)
			throw new RuntimeException("id==-1");
		return id;
	}
	
	public void modifyStat(String name, int absolute, int percental){
		
	}
	
	public boolean isDead(){
		return false;
	}
	
	
	public void takeDamage(int damage){
		this.modifyStat(StandardStats.HP, -damage, 1);
		//health -= damage;
		
		
		if(isDead()){			
			kill();
		}
		
		//dirty = true;*/
	}
	
	public void heal(int hpAdd){
		this.modifyStat(StandardStats.HP, +hpAdd, 1);
		/*float maxHp = getMaxHp();
		
		health += hpAdd;
		
		if(health>maxHp)
			health = maxHp;
		
		dirty = true;*/
	}
	
	public void addEnergy(int energy){
		this.modifyStat(StandardStats.EN, energy, 1);
		/*float maxEnergy = 1;
		
		this.energy += energy;
		
		if(this.energy>maxEnergy)
			this.energy = maxEnergy;
		if(this.energy < 0)
			this.energy = 0;
		
		dirty = true;*/
	}
	
	public float getMaxHp(){
		return 1;
	}
	
	public void kill(){
		Server.instance.getScene().removeObject(this);
	}
	

	
	/**
	 * When an object is flagged as dirty on the server, it is transmitted to the clients in range.
	 * There, it is copied over the existing object with this method.<p/>
	 * 
	 * An object may decide not to implement this method and return false to indicate that the copy process failed
	 * or did not take place. In this case the object is replaced by it's serialized server-copy.
	 * 
	 * @param o the copy destination 
	 * @return true if the copy succeeded
	 */
	public boolean copyTo(EngineObject o){
		return false;
	}
	
	public void dispose(){
		
	}

	public void moveTo(EngineObject to){
		x = to.x;
		y = to.y;
		z = to.z;
	}
	
	public float distanceTo(EngineObject to){
		float dx = x-to.x,
		      dy = y-to.y,
		      dz = z-to.z;
		
		return (float) Math.sqrt(dx*dx + dy*dy + dz*dz);
	}
	
	public PhysicsType getPhysicsType(){
		return physicsType;
	}
	
	public void notifyCollission(EngineObject other, Position pos){
		
	}
	
	public void notifyCellCollission(Position pos){
		
	}
	
	@Override
	public String toString(){
		return String.format("%s: %.1f %.1f %.1f", getClass().getSimpleName(), x, y, z);
	}
	
	@Override
	public int compareTo(EngineObject o) {
		return o==this?0:(int)((o.x-x)*1000);
	}
	
	public boolean equals(EngineObject o){
		return o==this;
	}
	

	public void radialGradientCircle(float[] innerColor, float[] outerColor, int slices, float radius)
	{
	      float incr = (float) (2 * Math.PI / slices);
	 
	      GL11.glBegin(GL11.GL_TRIANGLE_FAN);
	 
	      	      
          GL11.glColor4f(innerColor[0], innerColor[1], innerColor[2], innerColor[3]);
          GL11.glVertex2f(0.0f, 0.0f);
 
          GL11.glColor4f(outerColor[0], outerColor[1], outerColor[2], outerColor[3]);
 
          for(int i = 0; i < slices; i++)
          {
                float angle = incr * i;
                float x = (float) Math.cos(angle) * radius;
                float y = (float) Math.sin(angle) * radius;
 
                GL11.glVertex3f(x, 0, y);
          }
 
          GL11.glVertex3f(radius, 0, 0.0f);
	 
	      GL11.glEnd();
	}
}
